package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.config.EsConfig;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
    @Autowired
    private EsConfig esConfig;


    @Override
    public PageResult search (RequestParams params) {
        RestHighLevelClient client = esConfig.client ();
        SearchRequest request = new SearchRequest ("hotel");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery ();
        String key = params.getKey ();
        if (StringUtils.isBlank (key)) {
            boolQueryBuilder.must (QueryBuilders.matchAllQuery ());
        } else {
            boolQueryBuilder.must (QueryBuilders.matchQuery ("all", key));
            request.source ().highlighter (
                    new HighlightBuilder ().field ("name").requireFieldMatch (false)
            );
        }
        if (params.getBrand () != null && !"".equals (params.getBrand ())) {
            boolQueryBuilder.filter (QueryBuilders.termQuery ("brand", params.getBrand ()));
        }
        if (params.getCity () != null && !"".equals (params.getCity ())) {
            boolQueryBuilder.filter (QueryBuilders.termQuery ("city", params.getCity ()));
        }
        if (params.getStarName () != null && !"".equals (params.getStarName ())) {
            boolQueryBuilder.filter (QueryBuilders.termQuery ("starName", params.getStarName ()));
        }
        if (params.getMinPrice () != null && params.getMaxPrice () != null) {
            boolQueryBuilder.filter (QueryBuilders
                    .rangeQuery ("price")
                    .gte (params.getMinPrice ())
                    .lte (params.getMaxPrice ()));
        }
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery (boolQueryBuilder,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder (
                                QueryBuilders.termQuery ("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction (10)
                        )
                });
        request.source ().query (functionScoreQuery);
        int page = params.getPage ();
        int size = params.getSize ();
        request.source ().from ((page - 1) * size).size (size);

        //排序
        String location = params.getLocation ();
        if (location != null && !location.equals ("")) {
            request.source ().sort (SortBuilders.geoDistanceSort ("location",
                            new GeoPoint (location))
                    .order (SortOrder.ASC)
                    .unit (DistanceUnit.KILOMETERS));
        }
        String sortBy = params.getSortBy ();
        if (StringUtils.isNotBlank (sortBy)&&!"default".equals (sortBy)) {
            request.source ().sort (sortBy, SortOrder.ASC);
        }

        try {
            SearchResponse search = client.search (request, RequestOptions.DEFAULT);

            return handleResponse (search);
        } catch (IOException e) {
            throw new RuntimeException (e);
        }
    }


    private PageResult handleResponse (SearchResponse search) {
        SearchHits hits = search.getHits ();
        long value = hits.getTotalHits ().value;
        SearchHit[] hitsHits = hits.getHits ();
        ArrayList<HotelDoc> arrayList = new ArrayList<> ();
        if (hitsHits != null && hitsHits.length != 0) {
            for (SearchHit hitsHit : hitsHits) {
                String json = hitsHit.getSourceAsString ();
                HotelDoc hotelDoc = JSON.parseObject (json, HotelDoc.class);
                Object[] sortValues = hitsHit.getSortValues ();
                HighlightField name = hitsHit.getHighlightFields ().get ("name");
                if(name!=null){
                    String string = name.getFragments ()[0].string ();
                    hotelDoc.setName (string);
                }
                if(sortValues.length>0){
                    Object sortValue = sortValues[0];
                    hotelDoc.setDistance (sortValue);
                }
                arrayList.add (hotelDoc);
            }
        }
        return new PageResult (value, arrayList);

    }
}
